package sw6.girafplaceclient;

import girafPlace.lib.Application;

import java.io.*;
import java.net.*;
import java.util.ArrayList;

import android.app.Activity;
import android.content.*;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.os.Environment;
import org.apache.http.util.ByteArrayBuffer;
import sw6.launcher.R;
import sw6.launcher.TableInfo;
import sw6.launcher.TableInfo.Applications;

public class AppDetailsActivity extends Activity {
	/** The application to show. Is provided through the APP extra. */
	protected Application app;

	/** The default debug tag for the girafPlaceClient package */
	private final String tag = "GirafPlaceClient";

	/** The install state of the application */
	private State state = State.NEWAPP;

	/** The download progress in percent */
	private int progress;

	/** BrodcastReciever for installs */
	private BroadcastReceiver installReciever;

	/** BroadcastReciever for uninstalls */
	private BroadcastReceiver uninstallReciever;

	/** The version of the app when installed. If the app is not installed it is -1. */
	private int installedVersion = -1;



	@Override
	protected void onCreate(Bundle savedInstanceState) {
		app = (Application)getIntent().getExtras().get("APP");
		setContentView(R.layout.appdetails);

		//Setup
		setupState();
		setupBroadcast();

		//Async icon download
		new DownloadIcon().execute();
		super.onCreate(savedInstanceState);
	}

	/**
	 * Setups the state according to the installed version of the application. 
	 * Will get the installed version from the packagemanager.
	 */
	private void setupState() {
		if(state == State.DOWNLOADING && progress != 100)
			return;
		try {
			PackageInfo info = getPackageManager().getPackageInfo(app.getPackage(), 0);	
			installedVersion = info.versionCode;
			if(installedVersion != app.getVersion())
				state = State.UPDATEAPP;
			else
				state = State.OLDAPP;
		} catch(NameNotFoundException e) {
			installedVersion = -1;
			state = State.NEWAPP;
		}
		changeState();
	}

	/**
	 * Creates and registers the brodcast recievers for install and uninstall.
	 */
	private void setupBroadcast() {
		installReciever = new BroadcastReceiver() {		
			@Override
			public void onReceive(Context context, Intent intent) { //TODO This should check weather this is an update and use the old SSID list. Somehow
				int uid = (Integer) intent.getExtras().get(Intent.EXTRA_UID);
				Log.d(tag, getPackageManager().getNameForUid(uid) + app.getPackage());
				if(getPackageManager().getNameForUid(uid).compareTo(app.getPackage()) == 0) {
					try {
						installedVersion = getPackageManager().getPackageInfo(app.getPackage(), 0).versionCode;
						state = State.OLDAPP;
						changeState();

						ContentValues values = new ContentValues();


						try {
							ByteArrayOutputStream stream = new ByteArrayOutputStream();
							ObjectOutputStream out = new ObjectOutputStream( stream );

							out.writeObject(app);
							out.flush();

							values.put(Applications.APPLICATION_OBJECT, stream.toByteArray());

							stream.close();
							out.close();
						} catch (IOException e) {
							Log.e(tag,e.getMessage());
						}

						try{
							ByteArrayOutputStream stream = new ByteArrayOutputStream();
							ObjectOutputStream out = new ObjectOutputStream( stream );

							
							ArrayList<String> blackList = new ArrayList<String>();
							out.writeObject(blackList);
							out.flush();

							values.put(Applications.SSID_BLACKLIST, stream.toByteArray());

							stream.close();
							out.close();
						} catch (Exception e) {
							Log.e(tag,e.getMessage());
						}

						values.put(Applications.PACKAGE_NAME, app.getPackage());
						
						getContentResolver().insert(Uri.parse("content://" + TableInfo.AUTHORITY + "/applications"), values);

						Log.d(tag,String.format("The application %1$s has been installed.",getPackageManager().getNameForUid(uid)));
					} catch (NameNotFoundException e) {
						Log.e(tag, "WTF the newly installed application was not found?");
					}
				}

			}
		};

		uninstallReciever = new BroadcastReceiver() {		
			@Override
			public void onReceive(Context context, Intent intent) {
				Log.d(tag, intent.getData().toString());
				if(intent.getData().toString().compareTo("package:" + app.getPackage()) == 0) {
					installedVersion = -1;
					state = State.NEWAPP;
					changeState();
					
					getContentResolver().delete(Uri.parse("content://" + TableInfo.AUTHORITY + "/applications"), Applications.PACKAGE_NAME + " = '" + app.getPackage() + "'", null);
					
					Log.d(tag,"An application has been removed.");
				}
			}
		};

		IntentFilter installIntent = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
		installIntent.addDataScheme("package");

		IntentFilter uninstallIntent = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
		uninstallIntent.addDataScheme("package");

		registerReceiver(installReciever,installIntent);
		registerReceiver(uninstallReciever,uninstallIntent);
	}

	@Override
	protected void onDestroy() {
		unregisterReceiver(installReciever);
		unregisterReceiver(uninstallReciever);
		super.onDestroy();
	}

	@Override
	protected void onResume() {
		setupState();
		super.onResume();
	}

	/**
	 * Changes the state of the GUI according to the state. Will check the installed version to verify the set state and change accordingly.
	 */
	protected void changeState(){
		switch(state) {
		case NEWAPP:
			((TextView)findViewById(R.id.NameText)).setText(app.getName());
			TextView descText = (TextView)findViewById(R.id.DescriptionText);
			descText.setText(app.getDescription());
			((Button)findViewById(R.id.button)).setText("Install");
			((ProgressBar)findViewById(R.id.progressBar)).setVisibility(View.GONE);
			((ProgressBar)findViewById(R.id.progressBar)).setProgress(0);
			((Button)findViewById(R.id.button)).setEnabled(true);

			((Button)findViewById(R.id.button)).setOnClickListener(new OnClickListener() {

				@Override
				public void onClick(View v) {
					new DownloadApk().execute();
				}

			});
			break;
		case UPDATEAPP:
			((TextView)findViewById(R.id.NameText)).setText(app.getName());
			((TextView)findViewById(R.id.DescriptionText)).setText(app.getDescription());
			((Button)findViewById(R.id.button)).setText("Update");
			((ProgressBar)findViewById(R.id.progressBar)).setVisibility(View.GONE);
			((ProgressBar)findViewById(R.id.progressBar)).setProgress(0);
			((Button)findViewById(R.id.button)).setEnabled(true);

			((Button)findViewById(R.id.button)).setOnClickListener(new OnClickListener() {

				@Override
				public void onClick(View v) {
					new DownloadApk().execute();
				}

			});
			break;
		case DOWNLOADING:
			((TextView)findViewById(R.id.NameText)).setText(app.getName());
			((TextView)findViewById(R.id.DescriptionText)).setText("Description: " + app.getDescription());
			((Button)findViewById(R.id.button)).setText("Installing");
			((Button)findViewById(R.id.button)).setEnabled(false);

			((ProgressBar)findViewById(R.id.progressBar)).setVisibility(View.VISIBLE);
			((ProgressBar)findViewById(R.id.progressBar)).setProgress(progress);

			break;
		case OLDAPP:
			((TextView)findViewById(R.id.NameText)).setText(app.getName());
			((TextView)findViewById(R.id.DescriptionText)).setText("Description: " + app.getDescription());
			((Button)findViewById(R.id.button)).setText("Uninstall");
			((Button)findViewById(R.id.button)).setEnabled(true);

			((ProgressBar)findViewById(R.id.progressBar)).setVisibility(View.GONE);
			((ProgressBar)findViewById(R.id.progressBar)).setProgress(0);

			((Button)findViewById(R.id.button)).setOnClickListener(new OnClickListener() {

				@Override
				public void onClick(View v) {
					uninstallApp();
				}

			});
			break;
		default:
			break;

		}
	}

	/**
	 * Async task for downloading the icon from the server. 
	 * @author sw6c
	 *
	 */
	private class DownloadIcon extends AsyncTask<Void, Void, String> {

		@Override
		protected void onPreExecute() {
			Log.d(tag, "Preparing to start the download icon task");
			super.onPreExecute();
		}

		@Override
		protected String doInBackground(Void... voids) {
			try {
				Log.d(tag,"Starting the download task, first by building the string");
				String urlString = "http://girafplace.lcdev.dk/icos/";
				urlString = urlString + Integer.toString(app.getId()) + ".png"; // appName.apk

				Log.d(tag,String.format("The string is: %1$s",urlString));

				URL url = new URL(urlString);

				File file = new File(getCacheDir(), app.getName() + ".png");

				Log.d(tag,"Opening connection");
				URLConnection ucon = url.openConnection();

				ucon.connect();

				double size = ucon.getContentLength();
				Log.d(tag,String.format("The app has a reported size of %1$f", size));

				BufferedInputStream bis = new BufferedInputStream(ucon.getInputStream());

				ByteArrayBuffer baf = new ByteArrayBuffer(200);

				Log.d(tag,"Starting to read");
				int current = 0;
				while((current = bis.read()) != -1 && !isCancelled()) {
					baf.append((byte) current);
				}

				FileOutputStream fos = new FileOutputStream(file);
				fos.write(baf.toByteArray());
				fos.close();

				if(isCancelled())
					return null;
				else
					return file.getPath();
			} catch (Exception e) {
				Log.e(tag,e.getMessage());
			}
			return null;
		}

		@Override
		protected void onPostExecute(String result) {	
			if(result != null) {
				((ImageView)findViewById(R.id.imageView1)).setImageDrawable(new BitmapDrawable(result));
			}
		}
	}

	/**
	 * AsyncTask for downloading and initiating install of the application. 
	 * @author rune
	 *
	 */
	private class DownloadApk extends AsyncTask<Void, Double, String> {

		@Override
		protected void onPreExecute() {
			Log.d(tag, "Preparing to starting the download task");
			progress = 0;
			state = State.DOWNLOADING;
			changeState();

			super.onPreExecute();
		}

		@Override
		protected String doInBackground(Void... voids) {
			try {
				Log.d(tag,"Starting the download task, first by building the string");
				String urlString = "http://girafplace.lcdev.dk/installs/";
				urlString = urlString + Integer.toString(app.getId()) + ".apk"; // appName.apk


				Log.d(tag,String.format("The string is: %1$s",urlString));

				URL url = new URL(urlString);

				File file = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), app.getName() + ".apk");

				Log.d(tag,"Opening connection");
				URLConnection ucon = url.openConnection();

				ucon.connect();

				double size = ucon.getContentLength();
				Log.d(tag,String.format("The app has a reported size of %1$f", size));

				BufferedInputStream bis = new BufferedInputStream(ucon.getInputStream());

				ByteArrayBuffer baf = new ByteArrayBuffer(200);

				Log.d(tag,"Starting to read");
				int current = 0;
				double read = 0;
				while((current = bis.read()) != -1 && !isCancelled()) {
					baf.append((byte) current);
					read++;
					publishProgress((read/size)*100);
				}

				Log.d(tag,String.format("Read %1$f bytes from the stream", read));

				FileOutputStream fos = new FileOutputStream(file);
				fos.write(baf.toByteArray());
				fos.close();

				if(isCancelled())
					return null;
				else
					return file.getPath();
			} catch (Exception e) {
				Log.e(tag,e.getMessage());
			}
			return null;
		}

		@Override
		protected void onProgressUpdate(Double... values) {
			progress = values[0].intValue();
			((ProgressBar)findViewById(R.id.progressBar)).setProgress(progress);
			super.onProgressUpdate(values);
		}

		@Override
		protected void onCancelled() {
			state = State.NEWAPP;
			changeState();
			super.onCancelled();

		}

		@Override
		protected void onPostExecute(String result) {

			if(result != null) {
				try {
					Intent promptInstall = new Intent(Intent.ACTION_VIEW);
					promptInstall.setDataAndType(Uri.fromFile(new File(result)),"application/vnd.android.package-archive");
					startActivity(promptInstall);
				} catch (ActivityNotFoundException e){
					Log.e(tag, e.getMessage());
				}
			}
			else {
				state = State.NEWAPP;
				changeState();
			}
		}
	}

	/**
	 * Uninstalls the application if it is installed. 
	 */
	public void uninstallApp(){
		try{	
			Uri packageURI = Uri.parse("package:" + app.getPackage());
			Intent uninstallIntent = new Intent(Intent.ACTION_DELETE, packageURI);
			startActivity(uninstallIntent);
		} catch (ActivityNotFoundException e){
			Log.e(tag, e.getMessage());
		}
	}

	/**
	 * Enum for the different states of the application
	 * @author sw6c
	 *
	 */
	protected enum State {
		NEWAPP,
		UPDATEAPP,
		DOWNLOADING,
		OLDAPP;	
	}
}
